/*
 * COX1216.c
 *
 *  Created on: 2012/10/24
 *      Author: shenming
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "rule_info.h"
#include "core.h"
#include "rule_lib.h"
#include "trace.h"
#include "TraceAst.h"

static const PSZ RuleID = "COX1216";

#define NG_KIND_CANNOTCAL	1
#define NG_KIND_SIGN		2
#define NG_KIND_OVER		3

static const PSZ MESSAGE_ENG = "";
static const PSZ MESSAGE_JPN_CANNOTCAL = "定数書式内に加減乗除以外の演算子があり、逆算できません";
static const PSZ MESSAGE_JPN_SIGN = "物理値とＣＰＵ値から変換した物理値の符号が違います";
static const PSZ MESSAGE_JPN_OVER = "定数及びマップ点が25％以上桁落ちしています";
static const PSZ MESSAGE_CHS = "";

static const U2 RANK = MSG_RANK_ERR;

static PRULEOUT pRuleHead;
static PRULEOUT pRuleTail;

/******************************************************************
 * static function:
 * getExpVal
 *******************************************************************/
static DOUBLE getExpVal(AST exp) {
	INTVALUE ival;
	AST valueInfo = 0;
	if (exp <= 0) {
		return 0;
	}
	valueInfo = CExpression.getCalcuteValue(exp);
	if (CValueInfo.isFloating(valueInfo)) {
		return (DOUBLE) (CValueInfo.getFloatingValue(valueInfo));
	} else {
		ival = CValueInfo.getIntegerValue(valueInfo);
		if (ival.sign > 0) {
			return (DOUBLE) (ival.u8Remainder);
		} else {
			return (DOUBLE) (-ival.u8Remainder);
		}
	}
}

/******************************************************************
 * static function:
 * reverCal
 *******************************************************************/
static DOUBLE reverCal(AST exp, DOUBLE val, AST rightExp) {
	U4 kind = CExpression.getOpKind(exp);
	DOUBLE rightVal = 0;
	if (rightExp == 0) {
		return val;
	} else {
		rightVal = getExpVal(rightExp);
	}
	if (CAdditiveOpKind.isAdditive(kind)) {
		val = val - rightVal;
	} else if (CAdditiveOpKind.isSubtraction(kind)) {
		val = val + rightVal;
	} else if (CMultiOpKind.isMultiplicative(kind)) {
		val = val / rightVal;
	} else if (CMultiOpKind.isDivision(kind)) {
		val = val * rightVal;
	}
	return val;
}

/******************************************************************
 * static function:
 * calPhyVal
 *******************************************************************/
static VOID calPhyVal(AST exp, DOUBLE val, DOUBLE * phyVal,
		DOUBLE * phyValAfter) {
	AST childExp = 0;
	AST lsbExp = 0;
	AST phyofsExp = 0;
	AST phyExp = 0;
	AST ofsExp = 0;

	/*get casted exp*/
	exp = CExpression.getRightNode(exp);
	exp = getFstUnpChdExp(exp);

	/*get LSB*/
	exp = CExpression.getLeftNode(exp);
	exp = getFstUnpChdExp(exp);
	lsbExp = exp;

	/*get PHYOFS*/
	while (!CExpressionOpKind.isParentheseExp(CExpression.getOpKind(exp))) {
		exp = CExpression.getLeftNode(exp);
	}
	phyofsExp = exp;

	/*get PHY and OFS*/
	exp = getFstUnpChdExp(exp);
	if (CExpressionOpKind.isAdditiveOp(CExpression.getOpKind(exp))) {
		childExp = CExpression.getLeftNode(exp);
		while (!CExpressionOpKind.isParentheseExp(
				CExpression.getOpKind(childExp))) {
			childExp = CExpression.getLeftNode(childExp);
			if (childExp == 0) {
				break;
			}
		}
		if (childExp > 0) {
			phyExp = childExp;
			ofsExp = CExpression.getRightNode(exp);
		} else {
			phyExp = exp;
			ofsExp = 0;
		}
	} else {
		phyExp = exp;
		ofsExp = 0;
	}

	/*calculate phy value*/
	*phyVal = getExpVal(phyExp);
	while (lsbExp != phyofsExp) {
		val = reverCal(lsbExp, val, CExpression.getRightNode(lsbExp));
		lsbExp = CExpression.getLeftNode(lsbExp);
	}
	if (ofsExp) {
		val = reverCal(getFstUnpChdExp(phyofsExp), val, ofsExp);
	}
	*phyValAfter = val;
}

/******************************************************************
 * static function:
 * checkCastExp
 *******************************************************************/
static INT checkCastExp(AST exp) {
	DOUBLE castExpVal;
	DOUBLE phyVal;
	DOUBLE phyValAfter;
	DOUBLE dbVal;

	/*value of cast expr*/
	castExpVal = getExpVal(exp);

	/*calculate values*/
	calPhyVal(exp, castExpVal, &phyVal, &phyValAfter);

	/*check if NG_KIND_SIGN*/
	if ((phyVal >= 0) ^ (phyValAfter >= 0)) {
		return NG_KIND_SIGN;
	}

	/*check if NG_KIND_OVER*/
	if (phyVal == 0) {
		if (phyValAfter == 0) {
			dbVal = 100;
		} else {
			dbVal = 0;
		}
	} else {
		dbVal = phyValAfter / phyVal * 100;
	}
	if (dbVal < 0) {
		dbVal = -dbVal;
	}
	if (dbVal < 75) {
		return NG_KIND_OVER;
	}

	return RULE_RESULT_OK;
}

/******************************************************************
 * static function:
 * checkNG
 *******************************************************************/
static INT checkNG(AST exp) {
	INT result = RULE_RESULT_OK;
	U4 expKind = 0;
	exp = getFstUnpChdExp(exp);
	expKind = CExpression.getOpKind(exp);
	if (CExpressionOpKind.isCastOp(expKind)) {
		result = checkCastExp(exp);
	} else if (CAdditiveOpKind.isSubtraction(expKind)) {
		exp = CExpression.getRightNode(exp);
		exp = getFstUnpChdExp(exp);
		result = checkCastExp(exp);
	} else {
		return RULE_RESULT_OK;
	}

	return result;
}

/******************************************************************
 * main logic
 *******************************************************************/
static S4 RuleMain(U2 kind, AST index, U4 event) {
	INT result = RULE_RESULT_OK;
	PRULEOUT newNode = NULL;
	PRULEOUT tmpNode = NULL;
	PRULEOUT tmpNode1 = NULL;

	AST codeScp = 0;
	AST expInfo = 0;

	if (kind != TR_KIND_DECLARATION) {
		return EXIT_SUCCESS;
	}

	if (!(event & EVT_DECLARATION)) {
		return EXIT_SUCCESS;
	}

	if (!isConstInitExp(index, &expInfo)) {
		return EXIT_SUCCESS;
	}

	if (!isDnInitExp(expInfo)) {
		return EXIT_SUCCESS;
	}

	result = checkNG(expInfo);

	/*re-checked?*/
	tmpNode = pRuleHead;
	while (tmpNode != NULL ) {
		if (tmpNode->indexInfo == index) {
			if (result > RULE_RESULT_OK) {
				result = RULE_RESULT_OK;
			} else {
				if (tmpNode == pRuleHead) {
					pRuleHead = pRuleHead->next;
					free(tmpNode);
				} else if (tmpNode == pRuleTail) {
					tmpNode1->next = NULL;
					pRuleTail = tmpNode1;
					free(tmpNode);
				} else {
					tmpNode1->next = tmpNode->next;
					free(tmpNode);
				}
			}
			break;
		}
		tmpNode1 = tmpNode;
		tmpNode = tmpNode->next;
	}

	/*make result struct*/
	if (result > RULE_RESULT_OK) {
		newNode = (PRULEOUT) malloc(sizeof(RULEOUT));
		if (pRuleHead == NULL ) {
			pRuleHead = newNode;
		}

		codeScp = CExpression.getCodeScope(expInfo);
		newNode->indexInfo = index;
		newNode->fileIndex = CCodeScope.getFileIndex(codeScp);
		newNode->beginLine = CCodeScope.getBeginLine(codeScp);
		newNode->beginCol = CCodeScope.getBeginLineOffset(codeScp);
		newNode->endLine = CCodeScope.getEndLine(codeScp);
		newNode->endCol = CCodeScope.getEndLineOffset(codeScp);
		newNode->ruleID = RuleID;
		newNode->errRank = RANK;

		sprintf(newNode->msgBuf_eng, MESSAGE_ENG);
		sprintf(newNode->msgBuf_chs, MESSAGE_CHS);

		switch (result) {
		case NG_KIND_CANNOTCAL:
			sprintf(newNode->msgBuf_jpn, MESSAGE_JPN_CANNOTCAL);
			break;
		case NG_KIND_SIGN:
			sprintf(newNode->msgBuf_jpn, MESSAGE_JPN_SIGN);
			break;
		case NG_KIND_OVER:
			sprintf(newNode->msgBuf_jpn, MESSAGE_JPN_OVER);
			break;
		default:
			sprintf(newNode->msgBuf_jpn, " ");
			break;
		}

		newNode->rfileIndex = 0;
		newNode->rlocLine = 0;
		newNode->next = NULL;

		if (pRuleTail != NULL ) {
			pRuleTail->next = newNode;
		}

		pRuleTail = newNode;
	}

	return EXIT_SUCCESS;
}

VOID COX1216_register(VOID) {
	PRULEMAN * pCurRule = &stRuleManager;
	PRULEMAN newRuleMan = (PRULEMAN) malloc(sizeof(RULEMAN));
	memset(newRuleMan, 0, sizeof(RULEMAN));
	newRuleMan->ruleID = RuleID;
	newRuleMan->ruleSwitch = OFF;
	newRuleMan->headOut = &pRuleHead;
	newRuleMan->tailOut = &pRuleTail;
	newRuleMan->ruleMain_cmd = NULL;
	newRuleMan->ruleMain_ast = RuleMain;
	newRuleMan->nextRule = NULL;
	if (stRuleManager == NULL ) {
		stRuleManager = newRuleMan;
	} else {
		while ((*pCurRule)->nextRule != NULL ) {
			pCurRule = &((*pCurRule)->nextRule);
		}
		(*pCurRule)->nextRule = newRuleMan;
	}
}

